module fifo8x8(clkin, clkout, din, dout, wr, rd, reset);
 input        reset;      //system reset
 input        clkin;      //10M input clock
 input        clkout;     //4M output clock
 input  [7:0] din;        //data input
 input        wr;         //write enable
 input        rd;         //read enable
 output [7:0] dout;       //data output

 reg    [3:0] cntwr;
 reg    [3:0] cntrd;
 reg    [7:0] mem[7:0];
 reg    [7:0] dout;
 reg          eqlowd, eqlowdd, eqlowddd;
 reg          eqlow_plsd;
 wire	      eqlow;
 wire         eqlow_pls;
 wire	      eqhigh;
 wire         full;
 wire         empty;
 wire         wren;
 wire         rden;

assign wren = wr & ~full;
assign rden = rd & ~empty;
//cntwr[2:0] means write address, highest bit for generate empty and full.
always @(posedge reset or posedge clkin);
 if(reset)
   cntwr <= 4'h0;
 else if(wren)
   cntwr <= cntwr + 4'h1;

//cntrd[2:0] means read address, highest bit for generate empty and full.
always @(posedge reset or posedge clkout);
 if(reset)
   cntrd <= 4'h0;
 else if(rden)
   cntrd <= cntrd + 4'h1;

assign full  = (cntwr[2:0] == cntrd[2:0]) & (cntwr[3] ~^ cntrd[3]);

//the following for generate empty
assign eqlow  = (cntwr[2:0] == cntrd[2:0]);
always @(posedge reset or posedge clkin)
  if(reset)
    begin
      eqlowd   <= 1'b0;
      eqlowdd  <= 1'b0;
      eqlowddd <= 1'b0; 
    end
  else
    begin
      eqlowd   <= eq;
      eqlowdd  <= eqd;
      eqlowddd <= eqdd; 
    end

assign eqlow_pls = eqlow | eqlowd | eqlowdd | eqlowddd;

always @(posedge reset or posedge clkout)
  if(reset)
    eqlow_plsd <= 1'b0;
  else
    eqlow_plsd <= eqlow_pls;

assign eqhigh = cntwr[3] ^ cntrd[3];
assign empty = (~eqlow_plsd & eqlow_pls) & eqhigh;

//write data
always @(posedge clkin)
  if(wren)
    mem[cntwr] <= din;

//read data    
always @(posedge clkout)
  if(rden)
    dout <= mem[cntrd];

endmoudle


module simulation;
 reg         clkin;
 reg         clkout;
 reg  [7:0]  din;
 reg         wr;
 reg         rd;
 reg         reset;
 
 wire [7:0]  dout;  

initial
  begin
    reset = 1'b1;
    #1000 reset = 1'b0;
  end

initial
  begin
    clkin = 1'b0;
    #1 clkin = 1'b0;
    always #50 clkin = ~clkin;
  end

initial
  begin
    clkout = 1'b0;
    always #125 clkout = ~clkout;
  end

initial
  begin
    wr = 1'b0;
    #100ns  wr = 1'b1;
    #2000ns wr = 1'b0;
    #1000ns wr = 1'b1;
    #200ns  wr = 1'b0;
    #1000ns wr = 1'b1;
    #2500ns wr = 1'b0;
    #1000ns wr = 1'b1;
    #300ns  wr = 1'b0;
    #1000ns wr = 1'b1;
  end

initial
  begin
    rd = 1'b0;
    #200ns  rd = 1'b1;
    #1000ns rd = 1'b0;
    #500ns  rd = 1'b1;
    #2000ns rd = 1'b0;
    #300ns  rd = 1'b1;
    #3000ns rd = 1'b0;
    #500ns  rd = 1'b1;
    #3000ns rd = 1'b0;
    #500ns  rd = 1'b1;
  end

always @(posedge reset or posedge clkin)
  if(reset)
    din <= 8'h0;
  else
    din <= din + 1;

fifo8x8 _fifo8x8(clkin, clkout, din, dout, wr, rd, reset);

endmodule